#include "stdafx.h"
#include <windows.h>
#include <assert.h>
#include <map>
#include <queue>
#include <ctype.h>
#include <deque>
#include <queue>
#include <vector>

#if defined(_XBOX)
        #include "NxXBOX.h"
        #include <winsockx.h>
#endif

#if defined(WIN32)
        #ifndef _WIN32_WINNT
        #define _WIN32_WINNT 0x400
        #endif
        //#include <windows.h>
#endif

#if defined(WIN64)
        #ifndef _WIN64_WINNT
        #define _WIN64_WINNT 0x400
        #endif
        #include <windows.h>
#endif

#if defined(_XBOX)
        #include "NxXBOX.h"
#endif

#if defined(__APPLE__) || defined(LINUX)
        #include <pthread.h>
#endif

#include "telnet.h"

// Common Telnet Functionality.
#ifdef WIN32
#pragma warning(disable:4786 4996)
#pragma comment(lib,"wsock32.lib")
#endif

TELNET::Telnet *gTelnet=0; // optional global variable representing the TELNET singleton for the application.

namespace TELNET{

	//******************************************************
	//*** Mutex layer
	//******************************************************

	class OdfMutex{
			public:
					OdfMutex(void);
					~OdfMutex(void);

			public:
					// Blocking Lock.
					void Lock(void);

					// Non-blocking Lock. Return's false if already locked.
					bool TryLock(void);

					// Unlock.
					void Unlock(void);

			private:
					#if defined(WIN32) || defined(_XBOX)
					CRITICAL_SECTION m_Mutex;
					#elif defined(__APPLE__) || defined(LINUX)
					pthread_mutex_t  m_Mutex;
					#endif
	};

	OdfMutex::OdfMutex(void){
		#if defined(WIN32) || defined(WIN64) || defined(_XBOX)
				InitializeCriticalSection(&m_Mutex);
		#elif defined(__APPLE__) || defined(LINUX)
				pthread_mutex_init(&m_Mutex, 0);
		#endif
	}

	OdfMutex::~OdfMutex(void){
		#if defined(WIN32) || defined(WIN64) || defined(_XBOX)
				DeleteCriticalSection(&m_Mutex);
		#elif defined(__APPLE__) || defined(LINUX)
				pthread_mutex_destroy(&m_Mutex);
		#endif
	}

	// Blocking Lock.
	void OdfMutex::Lock(void){
		#if defined(WIN32) || defined(WIN64) || defined(_XBOX)
				EnterCriticalSection(&m_Mutex);
		#elif defined(__APPLE__) || defined(LINUX)
				pthread_mutex_lock(&m_Mutex);
		#endif
	}

	// Non-blocking Lock. Return's false if already locked.
	bool OdfMutex::TryLock(void){
		bool bRet = false;
		#if defined(WIN32) || defined(WIN64) || defined(_XBOX)
				bRet = TryEnterCriticalSection(&m_Mutex) ? true : false;
		#elif defined(__APPLE__) || defined(LINUX)
				pthread_mutex_trylock(&m_Mutex)
		#endif
				return bRet;
	}

	// Unlock.
	void OdfMutex::Unlock(void){
		#if defined(WIN32) || defined(WIN64) || defined(_XBOX)
				LeaveCriticalSection(&m_Mutex);
		#elif defined(__APPLE__) || defined(LINUX)
				pthread_mutex_unlock(&m_Mutex)
		#endif
	}

	//******************************************************
	//*** End Of Mutex layer
	//******************************************************

	//******************************************************
	//*** Threading layer
	//******************************************************

	class OdfThread{
			protected:
					// Called when the thread is started. This method runs on the Thread.
					virtual void OnThreadExecute(void)=0;

			public:
					OdfThread(void);
					~OdfThread(void);

			public:
					// Start execution of the thread.
					void ThreadExecute(void);

					// Wait for the Thread to terminate.
					void ThreadWait(void);

					// Stop the thread's execution (not safe).
					void ThreadKill(void);

			protected:
					// Safely Quit the thread.
					void ThreadQuit(void);

			private:
					HANDLE m_hThread;
					friend static DWORD WINAPI _ODFThreadFunc(LPVOID arg);
	};

	static DWORD WINAPI _ODFThreadFunc(LPVOID arg){
		((OdfThread *)arg)->OnThreadExecute();
		return 0;
	}

	OdfThread::OdfThread(void){
		m_hThread = 0;
	}

	OdfThread::~OdfThread(void){
		assert(!m_hThread);
	}

	// Start execution of the thread.
	void OdfThread::ThreadExecute(void){
		if(!m_hThread){
			m_hThread = CreateThread(0, 0, _ODFThreadFunc, this, 0, 0);
		}
	}

	// Wait for the Thread to terminate.
	void OdfThread::ThreadWait(void){
		if(m_hThread){
			WaitForSingleObject(m_hThread, INFINITE);
		}
	}

	// Safely Quit the thread.
	void OdfThread::ThreadQuit(void){
		if(m_hThread){
			m_hThread = 0;
			ExitThread(0);
		}
	}

	// Stop the thread's execution (not safe).
	void OdfThread::ThreadKill(void){
		if(m_hThread){
			#if defined(WIN32)
				TerminateThread(m_hThread, 0);
			#endif
			#if defined(_XBOX)
				//-- TODO: Please figure out the equivalent of TerminateThread
				assert(false);
			#endif
			CloseHandle(m_hThread);
			m_hThread = 0;
		}
	}

	//******************************************************
	//*** End of Threading layer
	//******************************************************

	//******************************************************
	//** The Telnet header file
	//******************************************************

	class TelnetLineNode;
	typedef std::queue<TelnetLineNode*> TelnetLineNodeQueue;

	#ifdef WIN32
		#pragma warning(push)
		#pragma warning(disable:4996)
	#endif

	class TelnetBlock{
		public:
			TelnetBlock(void){
				m_uiClient = 0;
				m_pcName = 0;
				m_pData  = 0;
				m_uiDataSize = 0;
			  }

			~TelnetBlock(void){
				Free();
			 }

			void setBlock(unsigned int client,const char *name,const void *mem,unsigned int len){
				Free();
				m_uiClient = client;
				if ( name ){
					size_t len = strlen(name);
					m_pcName = new char[len+1];
					strcpy(m_pcName,name);
				}
				if ( mem && len ){
					m_pData = new char[len];
					memcpy(m_pData,mem,len);
					m_uiDataSize = len;
				}
			}

			const void *GetData(void) {
				return m_pData; 
			}

			unsigned int GetSize(void) {
				return m_uiDataSize; 
			}

			void Free(void){
				if(m_pcName){
					delete [] m_pcName;
					m_pcName = 0;
				}
				if(m_pData){
					delete [] m_pData;
					m_pData = 0;
				}
			}
			unsigned int  m_uiClient;
			char   *m_pcName;
			void   *m_pData;
			unsigned int  m_uiDataSize;
	};

	typedef std::queue<TelnetBlock*> TelnetBlockQueue;

	// Common Telnet Functionality.
	class TelnetInterface{
			public:
					// Called when a connection has been established.
					virtual void OnConnect(unsigned int uiClient) {}

			public:
					// Sends text across the telnet connection.
					// returns false on failure.
					virtual bool SendText(unsigned int uiClient, const char *pcLine, ...)=0;

					// Sends a binary block of data across the telnet session.
					// returns false on failure.
					virtual bool SendBlock(const TelnetBlock &block)=0;

			public:
					// Waits until there is a block ready to be read.
					bool WaitForBlock(void);

					// Pops the last line off the local queue.
					// returns 0 if no lines available.
					const char *GetLine(unsigned int &uiClient);

					// Pops the last binary block off the local queue.
					// returns 0 if no lines available.
					bool GetBlock(TelnetBlock &block);

					void ReleaseBlock(TelnetBlock &block);

			protected:
					// Add a Line to the Local Queue.
					void PushLine(const char *pcLine, unsigned int uiClient);

					// Add a block to the local queue.
					void PushBlock(TelnetBlock *pBlock);

			protected:
					TelnetInterface(void);
					TelnetInterface(const TelnetInterface&){}
					~TelnetInterface(void);

			private:
					OdfMutex              m_LineMutex;
					TelnetLineNodeQueue   m_Lines;
					char                 *m_pcLineBuffer;

					OdfMutex              m_BlockMutex;
					OdfMutex              m_HaveBlock;
					TelnetBlockQueue      m_Blocks;
	};

	#ifdef WIN32
		#pragma warning(pop)
	#endif


	//******************************************************
	//** The Telnet header file
	//******************************************************

	class TelnetLineNode{
			public:
					char         *pcLine;
					unsigned int  uiClient;
	};

	// Waits until there is a block ready to be read.
	bool TelnetInterface::WaitForBlock(void){
			bool ret = false;
			if(!m_Blocks.empty()) return true;
			m_HaveBlock.Lock();
			if(!m_Blocks.empty()) ret = true;
			m_HaveBlock.Unlock();
			return ret;
	}

	// Pops the last line off the local queue.
	// returns 0 if no lines available.
	const char *TelnetInterface::GetLine(unsigned int &uiClient){
			const char *pRet = 0;
        
			m_LineMutex.Lock();
			if(!m_Lines.empty()){
					TelnetLineNode *node = m_Lines.front();
					m_Lines.pop();
                
					if(m_pcLineBuffer){
							::free(m_pcLineBuffer);
							m_pcLineBuffer = 0;
					}
                
					m_pcLineBuffer  = node->pcLine;
					uiClient        = node->uiClient;

					delete node;
                
					pRet            = m_pcLineBuffer;
			}
			m_LineMutex.Unlock();
        
			return pRet;
	}

	// Pops the last binary block off the local queue.
	// returns 0 if no lines available.
	bool  TelnetInterface::GetBlock(TelnetBlock &block){
			bool ret = false;
        
			m_BlockMutex.Lock();
			if(!m_Blocks.empty()){
					TelnetBlock *pBlock = m_Blocks.front();
					m_Blocks.pop();
					memcpy(&block, pBlock, sizeof(TelnetBlock));
					delete pBlock;
					ret = true;
			}
			m_BlockMutex.Unlock();

			if(m_Blocks.empty()){
					m_HaveBlock.TryLock();
			}

			return ret;
	}

	void TelnetInterface::ReleaseBlock(TelnetBlock &block){
			block.Free();
	}

	// Add a Line to the Local Queue.
	void TelnetInterface::PushLine(const char *pcLine, unsigned int uiClient){
			m_LineMutex.Lock();
			TelnetLineNode *node = new TelnetLineNode;
			node->pcLine = (char*)::malloc(sizeof(char)*(strlen(pcLine)+1));
			strcpy(node->pcLine, pcLine);
			node->uiClient = uiClient;
			m_Lines.push(node);
			m_LineMutex.Unlock();
	}

	// Add a block to the local queue.
	void TelnetInterface::PushBlock(TelnetBlock *pBlock){
			assert(pBlock);
			m_BlockMutex.Lock();
			m_Blocks.push(pBlock);
			m_BlockMutex.Unlock();

			m_HaveBlock.TryLock();
			m_HaveBlock.Unlock();
	}

	TelnetInterface::TelnetInterface(void){
			m_pcLineBuffer = 0;
			m_HaveBlock.Lock();
	}

	TelnetInterface::~TelnetInterface(void){
			if(m_pcLineBuffer){
					::free(m_pcLineBuffer);
			}

			while(!m_Lines.empty()){
					TelnetLineNode *pLine = m_Lines.front();
					::free(pLine->pcLine);
					delete pLine;
					m_Lines.pop();
			}

			m_HaveBlock.TryLock();
			m_HaveBlock.Unlock();
	}

	//************************************************************************************
	//** The Telnet Parser header file.
	//************************************************************************************

	typedef std::queue<char*> StringQueue;

	class _Block{
			public:
					void         *m_pData;
					unsigned int  m_uiDataSize;
	};

	typedef std::queue<_Block> BlockQueue;

	class TelnetParser{
			public:
					TelnetParser(void){
							m_uiBufferSize  = 1024;
							m_uiBufferUsed  = 0;
							m_pBuffer       = (char *)::malloc(sizeof(char)*m_uiBufferSize);
							m_pcLastLine    = 0;
							m_uiCurrBlockSize = 1024;
							m_uiCurrBlockUsed = 0;
							m_pCurrBlock      = (char *)::malloc(sizeof(char)*m_uiCurrBlockSize);
							m_bBlockMode      = false;
					}
                
					~TelnetParser(void){
							if(m_pBuffer){
								::free(m_pBuffer);
							}
                        
							if(m_pcLastLine){
									::free(m_pcLastLine);
							}

							if(m_pCurrBlock){
									::free(m_pCurrBlock);
							}
                        
							while(!m_Lines.empty()){
									char *pTemp = m_Lines.front();
									m_Lines.pop();
									if(pTemp){
											::free(pTemp);
									}
							}
                        
							while(!m_Blocks.empty()){
									_Block &block = m_Blocks.front();
									if(block.m_pData){
											::free(block.m_pData);
									}
									m_Blocks.pop();
							}
					}
         
			public:
					void AddBuffer(const char *pcBuffer, unsigned int uiLen){
							unsigned int uiNewSize = uiLen + m_uiBufferUsed;
							if(uiNewSize >= m_uiBufferSize) Resize(uiNewSize);
                        
							for(unsigned int i=0; i<uiLen; i++){
									if(m_bBlockMode)
											BlockChar(pcBuffer[i]);
									else
											ParseChar(pcBuffer[i]);
							}
					}
                
					const char *GetLine(void){
							const char *pRet = 0;
                        
							if(!m_Lines.empty()){
									if(m_pcLastLine){
											::free(m_pcLastLine);
									}
									m_pcLastLine = m_Lines.front();
									m_Lines.pop();
									pRet = m_pcLastLine;
							}
                        
							return pRet;
					}
                
					const void *GetBlock(unsigned int &uiSize){
							void *pRet = 0;
							if(!m_Blocks.empty()){
									_Block &block = m_Blocks.front();
									pRet = block.m_pData;
									uiSize = block.m_uiDataSize;
									m_Blocks.pop();
							}
							return pRet;
					}
        
			private:
					void ParseChar(char c){
							if(c >= 32 && c < 128){
									// simply add the character.
									m_pBuffer[m_uiBufferUsed++] = c;
							}else if(c == '\n' && m_uiBufferUsed){
									// Add a line to the queue.
									char *pcLine = (char*)::malloc(sizeof(char)*(m_uiBufferUsed+1));
									memcpy(pcLine, m_pBuffer, m_uiBufferUsed);
									pcLine[m_uiBufferUsed] = 0;
									m_Lines.push(pcLine);
									m_uiBufferUsed = 0;
							}else if(c == 8 && m_uiBufferUsed){
									// if backspace then remove the last character.
									m_uiBufferUsed--;
							}else{// ???
							}
                        
							m_pBuffer[m_uiBufferUsed] = 0;
                        
							char *pBlockStart = strstr(m_pBuffer, "<NxBlock");
							if(pBlockStart){
									char *equals  = strchr(pBlockStart, '=');
									char *end     = strchr(pBlockStart, '>');
									if(end){
											if(equals && equals < end){
													char *name = equals+1;
													unsigned int len = (unsigned int)(end - name) + 1;
											}
											m_bBlockMode = true;
									}
							}
                        
					}
                
					void BlockChar(char c){
							if(m_uiCurrBlockUsed+1 >=  m_uiCurrBlockSize){
									unsigned int uiNewSize = m_uiCurrBlockSize * 2;
									char *pBlock = (char*)::malloc(sizeof(char)*uiNewSize);
									memcpy(pBlock, m_pCurrBlock, m_uiCurrBlockUsed);
									if(m_pCurrBlock){
											::free(m_pCurrBlock);
									}
									m_pCurrBlock = pBlock;
									m_uiCurrBlockSize = uiNewSize;
							}
                        
							m_pCurrBlock[m_uiCurrBlockUsed++] = c;
							m_pCurrBlock[m_uiCurrBlockUsed]   = 0;
                        
							char *pcEnd = strstr(m_pCurrBlock, "</NxBlock>");
							if(pcEnd){
									m_bBlockMode = false;
									unsigned int uiSize = (unsigned int)(pcEnd - m_pCurrBlock);
                                
									_Block block;
									block.m_uiDataSize  = uiSize;
									block.m_pData       = (char*)::malloc(sizeof(char)*uiSize);
									memcpy(block.m_pData, m_pCurrBlock, uiSize);
									m_Blocks.push(block);
                                
									for(unsigned int i=uiSize+9; i<m_uiCurrBlockUsed; i++){
											ParseChar(m_pCurrBlock[i]);
									}
                                
									m_uiCurrBlockUsed = 0;
							}
					}
                
					void Resize(unsigned int uiNewSize){
							char *pNewBuffer = (char*)::malloc(sizeof(char)*uiNewSize);
							if(m_uiBufferUsed){
									memcpy(pNewBuffer, m_pBuffer, m_uiBufferUsed);
							}
							if(m_pBuffer){
									::free(m_pBuffer);
							}
							m_pBuffer = pNewBuffer;
							m_uiBufferSize = uiNewSize;
					}
        
			private:
					char             *m_pBuffer;
					unsigned int      m_uiBufferSize;
					unsigned int      m_uiBufferUsed;
                
					char             *m_pcLastLine;
					StringQueue       m_Lines;
                
					bool              m_bBlockMode;
					char             *m_pCurrBlock;
					unsigned int      m_uiCurrBlockSize;
					unsigned int      m_uiCurrBlockUsed;
					BlockQueue        m_Blocks;
	};



	//****************************************************************************************
	//** Telnet Client header file
	//****************************************************************************************


	// Simple Telnet Client.
	class TelnetClient : public TelnetInterface{
			public:
					TelnetClient(void);
					~TelnetClient(void);

			public:
					// Connect to a remote Telnet server.
					// returns false on failure.
					bool Connect(const char *pcAddress, unsigned short uiPort);
					// Closes the current connection.
					void Close(void);
					// Sends text across the telnet connection.
					// returns false on failure.
					virtual bool SendText(unsigned int uiClient, const char *pcLine, ...);
					// Sends a binary block of data across the telnet session.
					// returns false on failure.
					virtual bool SendBlock(const TelnetBlock &block);
			private:
					void ThreadFunc(void);
					friend DWORD WINAPI _TelnetClientFunc(LPVOID arg);

			private:
					TelnetClient(const TelnetClient &){}

			private:
					OdfMutex  m_Mutex;
					SOCKET    m_Socket;
					HANDLE    m_Thread;
	};

	//*************************************************************************************
	//** The Telnet Client source code
	//*************************************************************************************

	static DWORD WINAPI _TelnetClientFunc(LPVOID arg){
		((TelnetClient *)arg)->ThreadFunc();
		ExitThread(0);
		return 0;
	}


	void TelnetClient::ThreadFunc(void){
			bool bDone = false;

			m_Mutex.Lock();
			SOCKET  clientSocket  = m_Socket;
			bDone = clientSocket == INVALID_SOCKET ? true : false;
			m_Mutex.Unlock();
        
			#define BUFFER_SIZE 8192
			char vcBuffer[BUFFER_SIZE+1];
			TelnetParser parser;
        
        
			while(!bDone){
					int iBytesRead = 0;
					iBytesRead = recv(clientSocket, vcBuffer, BUFFER_SIZE, 0);

					if(iBytesRead <= 0){
							bDone = true;
							m_Mutex.Lock();
							closesocket(clientSocket);
							clientSocket = INVALID_SOCKET;
							m_Socket = clientSocket;
							m_Mutex.Unlock();
					}else{
							parser.AddBuffer(vcBuffer, iBytesRead);
					}
                
					const char *pcLine = 0;
					while((pcLine = parser.GetLine())){
							PushLine(pcLine, 0);
					}
                
					const void *pBlockData = 0;
					unsigned int uiBlockSize = 0;
					while((pBlockData = parser.GetBlock(uiBlockSize))){
							TelnetBlock *pBlock   = new TelnetBlock;
							pBlock->m_pData       = (void *)pBlockData;
							pBlock->m_uiDataSize  = uiBlockSize;
							pBlock->m_uiClient    = 0;
							pBlock->m_pcName      = 0;
							PushBlock(pBlock);
					}
                
					m_Mutex.Lock();
					clientSocket  = m_Socket;
					bDone = clientSocket == INVALID_SOCKET ? true : false;
					m_Mutex.Unlock();
			}
        
	}

	/*****************
	** TelnetClient **
	*****************/

	TelnetClient::TelnetClient(void){
			m_Socket  = INVALID_SOCKET;
			m_Thread  = 0;

			WSADATA data;
			WSAStartup(MAKEWORD(2, 2), &data);
	}

	TelnetClient::~TelnetClient(void){
			 Close();
	}

	// Connect to a remote Telnet server.
	// returns false on failure.
	bool TelnetClient::Connect(const char *pcAddress, unsigned short uiPort){
			bool bRet = false;
			Close();

			m_Thread = 0;

			#if defined(WIN32)
					m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
					if(m_Socket != INVALID_SOCKET){
							sockaddr_in addr = {0};
							addr.sin_family       = AF_INET;
							addr.sin_port         = htons(uiPort);
							addr.sin_addr.s_addr    = inet_addr(pcAddress);
                
							if(addr.sin_addr.s_addr == INADDR_NONE){
									hostent *pHost = gethostbyname(pcAddress);
									memcpy(&addr.sin_addr, pHost->h_addr, pHost->h_length);
									addr.sin_family = pHost->h_addrtype;
							}
                
							if(connect(m_Socket, (sockaddr *)&addr, sizeof(addr)) == 0){
									OnConnect(0);
									m_Thread = CreateThread(0, 0, _TelnetClientFunc, this, 0, 0);
									bRet = true;
							}else{
									assert(0);
							}
                
							if(!m_Thread){
									int error = WSAGetLastError();
									closesocket(m_Socket);
									m_Socket = INVALID_SOCKET;
							}
					}else{
							assert(0);
					}
			#endif
        
			return bRet;
	}

	// Closes the current connection.
	void TelnetClient::Close(void){
			if(m_Socket != INVALID_SOCKET){
					// Close the Socket.
					m_Mutex.Lock();
					closesocket(m_Socket);
					m_Socket = INVALID_SOCKET;
					m_Mutex.Unlock();
                
					// Wait for the Listen Thread to stop.
					WaitForSingleObject(m_Thread, INFINITE);
					m_Thread = 0;
			}
	}

	// Sends text across the telnet connection.
	// returns false on failure.
	bool TelnetClient::SendText(unsigned int uiClient, const char *pcLine, ...){
			char vcBuffer[8192];
			_vsnprintf(vcBuffer,8191, pcLine, (va_list)(&pcLine+1));
			unsigned int uiLen = (unsigned int)strlen(vcBuffer);
        
			send(m_Socket, vcBuffer, uiLen, 0);
        
			return true;
	}

	// Sends a binary block of data across the telnet session.
	// returns false on failure.
	bool TelnetClient::SendBlock(const TelnetBlock &block){
			bool          bRet        = false;
			char          vcFooter[]  = "</NxBlock>\r\n";
			char          vcHeader[256];
        
			sprintf(vcHeader, "\r\n<NxBlock=%s>", block.m_pcName);
        
			send(m_Socket, vcHeader, (int)strlen(vcHeader),0);

			// Send in small chunks...
			const char *pData = (const char*)block.m_pData;
			unsigned int uiDataSent = 0;
			while(uiDataSent < block.m_uiDataSize){
					unsigned int uiSendSize = block.m_uiDataSize - uiDataSent;
					uiSendSize = uiSendSize > 1024 ? 1024 : uiSendSize;
					int iSent = send(m_Socket, &pData[uiDataSent], uiSendSize, 0);
					uiDataSent += (unsigned int)iSent;
			} 
        
			send(m_Socket, vcFooter,(int)strlen(vcFooter),   0);
			bRet = true;
        
			return bRet;
	}

	//****************************************************************************
	//** Telnet server header file
	//***************************************************************************


	class TelnetServer_Client;
	typedef std::map<unsigned int, TelnetServer_Client*> TelnetServer_ClientMap;

	// Simple Telnet Server.
	class TelnetServer : public TelnetInterface{
			friend class TelnetServer_Client;

			public:
					TelnetServer(void);
					~TelnetServer(void);

			public:
					// Starts the Telnet server listening on
					// the specified port.
					// returns false if failure.
					bool Listen(unsigned short uiPort);

					// Closes all connections.
					void Close(void);

			public:
					// Sends text across the telnet connection.
					// returns false on failure.
					virtual bool SendText(unsigned int uiClient, const char *pcLine, ...);

					// Sends a binary block of data across the telnet session.
					// returns false on failure.
					virtual bool SendBlock(const TelnetBlock &block);

			private:
					void ThreadFunc(void);
					friend DWORD WINAPI _TelnetServerFunc(LPVOID arg);

			private:
					TelnetServer(const TelnetServer &){}

			private:
					unsigned int            m_uiLastClient;

					OdfMutex                m_ListenMutex;
					SOCKET                  m_ListenSocket;
					HANDLE                  m_ListenThread;

					TelnetServer_ClientMap  m_Clients;
	};

	//****************************************************************************
	//** Telnet server source
	//****************************************************************************

	class TelnetServer_Client{
			public:
					TelnetServer *m_pParent;
					unsigned int  m_uiClient;
					OdfMutex      m_Mutex;
					SOCKET        m_Socket;
					HANDLE        m_Thread;
        
			public:
					~TelnetServer_Client(void){
							TelnetServer_ClientMap::iterator iter;
							iter = m_pParent->m_Clients.find(m_uiClient);
                        
							if(iter != m_pParent->m_Clients.end()){
									m_pParent->m_Clients[m_uiClient] = 0;
									//m_pParent->m_Clients.erase(iter);
							}
					}
        
			private:
					void ThreadFunc(void);
					friend DWORD WINAPI _TelnetServerClientFunc(LPVOID arg);
	};


	/**********************
	** Threading Support **
	**********************/

	static DWORD WINAPI _TelnetServerClientFunc(LPVOID arg){
			TelnetServer_Client *pClient = (TelnetServer_Client *)arg;
			pClient->ThreadFunc();
			delete pClient;
			ExitThread(0);
			return 0;
	}

	static DWORD WINAPI _TelnetServerFunc(LPVOID arg){
			((TelnetServer *)arg)->ThreadFunc();
			ExitThread(0);
			return 0;
	}

	void TelnetServer_Client::ThreadFunc(void){
			bool bDone = false;
			OdfMutex &mutex = m_Mutex;
        
			#define BUFFER_SIZE 8192
			char vcBuffer[BUFFER_SIZE];
        
			TelnetParser parser;
        
			mutex.Lock();
			SOCKET  clientSocket  = m_Socket;
			bDone = clientSocket == INVALID_SOCKET ? true : false;
			mutex.Unlock();
        
			while(!bDone){
				int iBytesRead=0;

				#if !defined(_XBOX)
				iBytesRead = recv(clientSocket, vcBuffer, BUFFER_SIZE, 0);
				#endif
                
				if(iBytesRead > 0){
						parser.AddBuffer(vcBuffer, iBytesRead);
				}

				const char *pcLine = 0;
                
				while((pcLine = parser.GetLine())){
						m_pParent->PushLine(pcLine, m_uiClient);
				}
                
				mutex.Lock();
				clientSocket  = m_Socket;
				bDone = clientSocket == INVALID_SOCKET ? true : false;
				mutex.Unlock();
			}
	}

	void TelnetServer::ThreadFunc(void){
			bool bDone = false;
        
			m_ListenMutex.Lock();
			SOCKET listenSocket = m_ListenSocket;
			bDone = listenSocket == INVALID_SOCKET ? true : false;
			m_ListenMutex.Unlock();
        
			while(!bDone){
					SOCKET clientSocket= INVALID_SOCKET;
					#if !defined(_XBOX)
						clientSocket = accept(listenSocket, 0, 0);
					#endif
					if(clientSocket != INVALID_SOCKET){
							TelnetServer_Client *pClient = new TelnetServer_Client;
							pClient->m_pParent  = this;
							pClient->m_uiClient = ++m_uiLastClient;
							pClient->m_Socket   = clientSocket;
							m_Clients[pClient->m_uiClient] = pClient;
							pClient->m_Thread   = CreateThread(0, 0, _TelnetServerClientFunc, pClient, 0, 0);
                        
							OnConnect(pClient->m_uiClient);
					}
                
					m_ListenMutex.Lock();
					listenSocket = m_ListenSocket;
					bDone = listenSocket == INVALID_SOCKET ? true : false;
					m_ListenMutex.Unlock();
			}
        
			TelnetServer_ClientMap::iterator iter;
			for(iter=m_Clients.begin(); iter!=m_Clients.end(); iter++){
					TelnetServer_Client *pClient = (*iter).second;
					if(pClient){
							OdfMutex &mutex = pClient->m_Mutex;
                        
							// Signal the socket.
							mutex.Lock();
							#if !defined(_XBOX)
								closesocket(pClient->m_Socket);                 
							#endif
							pClient->m_Socket = INVALID_SOCKET;
							mutex.Unlock();
                        
							// Wait for the thread to stop.
							WaitForSingleObject(pClient->m_Thread, INFINITE);
					}
			}
			m_Clients.clear();
        
	}

	/*****************
	** TelnetServer **
	*****************/

	TelnetServer::TelnetServer(void){
			m_uiLastClient = 1;

			m_ListenSocket  = INVALID_SOCKET;
			m_ListenThread  = 0;

			#if !defined(_XBOX)
				WSADATA data;
				WSAStartup(MAKEWORD(2, 2), &data);
			#endif
	}

	TelnetServer::~TelnetServer(void){
			Close();
	}

	// Starts the Telnet server listening on
	// the specified port.
	// returns false if failure.
	bool TelnetServer::Listen(unsigned short uiPort){
			bool bRet = false;

			Close();
        
			m_ListenThread  = 0;

			#if !defined(_XBOX)
				m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        
				if(m_ListenSocket != INVALID_SOCKET){
						sockaddr_in addr      = {0};
						addr.sin_family       = AF_INET;
						addr.sin_port         = htons(uiPort);
						addr.sin_addr.s_addr  = htonl(INADDR_ANY);
                
						if(bind(m_ListenSocket, (sockaddr*)&addr, sizeof(addr)) == 0){
								if(listen(m_ListenSocket, SOMAXCONN) == 0){
										m_ListenThread = CreateThread(0, 0, _TelnetServerFunc, this, 0, 0);
										bRet = true;
								}else{
										int error = WSAGetLastError();
										assert(0);
								}
						}else{
								int error = WSAGetLastError();
						}
                
						if(!m_ListenThread){
								int error = WSAGetLastError();
								closesocket(m_ListenSocket);
								m_ListenSocket = INVALID_SOCKET;
						}
				}else{
						int error = WSAGetLastError();
						assert(0);
				}
			#endif

        
			return bRet;
	}

	// Closes all connections.
	void TelnetServer::Close(void){
		if(m_ListenSocket != INVALID_SOCKET){
				// Close the Socket.
				m_ListenMutex.Lock();
				#if !defined(_XBOX)             
					closesocket(m_ListenSocket);
				#endif
				m_ListenSocket = INVALID_SOCKET;
				m_ListenMutex.Unlock();
                
				// Wait for the Listen Thread to stop.
				WaitForSingleObject(m_ListenThread, INFINITE);
				m_ListenThread = 0;
		}
	}

	// Sends text across the telnet connection.
	// returns false on failure.
	bool TelnetServer::SendText(unsigned int uiClient, const char *pcLine, ...){
			bool bRet = false;
        
			TelnetServer_ClientMap::iterator iter;
        
			char vcBuffer[8192];
			_vsnprintf(vcBuffer, 8191, pcLine, (va_list)(&pcLine+1));
			unsigned int uiLen = (unsigned int)strlen(vcBuffer);
        
			if(!uiClient){
					for(iter=m_Clients.begin(); iter!=m_Clients.end(); iter++){
							TelnetServer_Client *pClient  = (*iter).second;
							pClient->m_Mutex.Lock();
							#if !defined(_XBOX)
								send(pClient->m_Socket, vcBuffer, uiLen, 0);
							#endif
							pClient->m_Mutex.Unlock();
							bRet = true;
					}
			}else{
					iter = m_Clients.find(uiClient);
					if(iter != m_Clients.end()){
							TelnetServer_Client *pClient  = (*iter).second;
							pClient->m_Mutex.Lock();
							#if !defined(_XBOX)                     
								send(pClient->m_Socket, vcBuffer, uiLen, 0);
							#endif
							pClient->m_Mutex.Unlock();
							bRet = true;
					}
			}
        
			return bRet;
	}

	// Sends a binary block of data across the telnet session.
	// returns false on failure.
	bool TelnetServer::SendBlock(const TelnetBlock &block){
			unsigned int  uiClient    = block.m_uiClient;
			bool          bRet        = false;
			char          vcFooter[]  = "</NxBlock>\r\n";
			char          vcHeader[256];
        
			sprintf(vcHeader, "\r\n<NxBlock=%s>", block.m_pcName);

			TelnetServer_ClientMap::iterator iter;
        
			if(!uiClient){
					for(iter=m_Clients.begin(); iter!=m_Clients.end(); iter++){
							TelnetServer_Client *pClient  = (*iter).second;
							pClient->m_Mutex.Lock();
							#if !defined(_XBOX)                     
								send(pClient->m_Socket, vcHeader,               (int)strlen(vcHeader),   0);
								send(pClient->m_Socket, (char*)block.m_pData,   block.m_uiDataSize, 0);
								send(pClient->m_Socket, vcFooter,               (int)strlen(vcFooter),   0);
							#endif
							pClient->m_Mutex.Unlock();
							bRet = true;
					}
			}else{
					iter = m_Clients.find(uiClient);
					if(iter != m_Clients.end()){
							TelnetServer_Client *pClient  = (*iter).second;
							pClient->m_Mutex.Lock();
                        
							#if !defined(_XBOX)
								send(pClient->m_Socket, vcHeader,               (int)strlen(vcHeader),   0);
                        
								// Send in small chunks...
								const char *pData = (const char*)block.m_pData;
								unsigned int uiDataSent = 0;
								while(uiDataSent < block.m_uiDataSize){
										unsigned int uiSendSize = block.m_uiDataSize - uiDataSent;
										uiSendSize = uiSendSize > 1024 ? 1024 : uiSendSize;
										int iSent = send(pClient->m_Socket, &pData[uiDataSent], uiSendSize, 0);
										uiDataSent += (unsigned int)iSent;
								}

								send(pClient->m_Socket, vcFooter,               (int)strlen(vcFooter),   0);
							#endif


							pClient->m_Mutex.Unlock();
							bRet = true;
					}
			}

			return bRet;
	}

	#if defined(__APPLE__) || defined(__CELLOS_LV2__) || defined(LINUX)
		#define stricmp(a, b) strcasecmp((a), (b))
	#endif

	/*******************************************************************/
	/******************** InParser.h  ********************************/
	/*******************************************************************/
	class InPlaceParserInterface{
	public:
			virtual int ParseLine(int lineno,int argc,const char **argv) =0;  // return TRUE to continue parsing, return FALSE to abort parsing process
	};

	enum SeparatorType{
			ST_DATA,        // is data
			ST_HARD,        // is a hard separator
			ST_SOFT,        // is a soft separator
			ST_EOS          // is a comment symbol, and everything past this character should be ignored
	};

	class InPlaceParser{
		public:
				InPlaceParser(void){
						Init();
				}

				InPlaceParser(char *data,int len){
						Init();
						SetSourceData(data,len);
				}

				InPlaceParser(const char *fname){
						Init();
						SetFile(fname);
				}

				~InPlaceParser(void);

				void Init(void){
						mQuoteChar = 34;
						mData = 0;
						mLen  = 0;
						mMyAlloc = false;
						for (int i=0; i<256; i++){
								mHard[i] = ST_DATA;
								mHardString[i*2] = (char)i;
								mHardString[i*2+1] = 0;
						}
						mHard[0]  = ST_EOS;
						mHard[32] = ST_SOFT;
						mHard[9]  = ST_SOFT;
						mHard[13] = ST_SOFT;
						mHard[10] = ST_SOFT;
				}

				void SetFile(const char *fname); // use this file as source data to parse.

				void SetSourceData(char *data,int len){
						mData = data;
						mLen  = len;
						mMyAlloc = false;
				};

				int  Parse(InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason

				int ProcessLine(int lineno,char *line,InPlaceParserInterface *callback);

				const char ** GetArglist(char *source,int &count); // convert source string into an arg list, this is a destructive parse.

				void SetHardSeparator(char c){ // add a hard separator
						mHard[c] = ST_HARD;
				}

				void SetHard(char c){ // add a hard separator
						mHard[c] = ST_HARD;
				}

				void SetCommentSymbol(char c){ // comment character, treated as 'end of string'
						mHard[c] = ST_EOS;
				}

				void ClearHardSeparator(char c){
						mHard[c] = ST_DATA;
				}

				void DefaultSymbols(void); // set up default symbols for hard seperator and comment symbol of the '#' character.

				bool EOS(char c){
						if ( mHard[c] == ST_EOS ){
								return true;
						}
						return false;
				}

				void SetQuoteChar(char c){
						mQuoteChar = c;
				}

		private:
				inline char * AddHard(int &argc,const char **argv,char *foo);
				inline bool   IsHard(char c);
				inline char * SkipSpaces(char *foo);
				inline bool   IsWhiteSpace(char c);
				inline bool   IsNonSeparator(char c); // non seperator,neither hard nor soft

				bool   mMyAlloc; // whether or not *I* allocated the buffer and am responsible for deleting it.
				char  *mData;  // ascii data to parse.
				int    mLen;   // length of data
				SeparatorType  mHard[256];
				char   mHardString[256*2];
				char           mQuoteChar;
	};

	/*******************************************************************/
	/******************** InParser.cpp  ********************************/
	/*******************************************************************/
	void InPlaceParser::SetFile(const char *fname){
			if ( mMyAlloc ){
					::free(mData);
			}
			mData = 0;
			mLen  = 0;
			mMyAlloc = false;

			FILE *fph = fopen(fname,"rb");
			if ( fph ){
					fseek(fph,0L,SEEK_END);
					mLen = ftell(fph);
					fseek(fph,0L,SEEK_SET);
					if ( mLen ){
							mData = (char *) ::malloc(sizeof(char)*(mLen+1));
							int ok = (int)fread(mData, mLen, 1, fph);
							if ( !ok ){
									::free(mData);
									mData = 0;
							}else{
									mData[mLen] = 0; // zero byte terminate end of file marker.
									mMyAlloc = true;
							}
					}
					fclose(fph);
			}
	}

	InPlaceParser::~InPlaceParser(void){
			if ( mMyAlloc ){
					::free(mData);
			}
	}

	#define MAXARGS 512

	bool InPlaceParser::IsHard(char c){
			return mHard[c] == ST_HARD;
	}

	char * InPlaceParser::AddHard(int &argc,const char **argv,char *foo){
			while ( IsHard(*foo) ){
					const char *hard = &mHardString[*foo*2];
					if ( argc < MAXARGS ){
							argv[argc++] = hard;
					}
					foo++;
			}
			return foo;
	}

	bool   InPlaceParser::IsWhiteSpace(char c){
			return mHard[c] == ST_SOFT;
	}

	char * InPlaceParser::SkipSpaces(char *foo){
			while ( !EOS(*foo) && IsWhiteSpace(*foo) ) foo++;
			return foo;
	}

	bool InPlaceParser::IsNonSeparator(char c){
			return ( !IsHard(c) && !IsWhiteSpace(c) && c != 0 );
	}


	int InPlaceParser::ProcessLine(int lineno,char *line,InPlaceParserInterface *callback){
			int ret = 0;

			const char *argv[MAXARGS];
			int argc = 0;

			char *foo = line;

			while ( !EOS(*foo) && argc < MAXARGS ){

					foo = SkipSpaces(foo); // skip any leading spaces

					if ( EOS(*foo) ) break;

					if ( *foo == mQuoteChar ){ // if it is an open quote
							foo++;
							if ( argc < MAXARGS ){
									argv[argc++] = foo;
							}
							while ( !EOS(*foo) && *foo != mQuoteChar ) foo++;
							if ( !EOS(*foo) ){
									*foo = 0; // replace close quote with zero byte EOS
									foo++;
							}
					}else{

							foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces

							if ( IsNonSeparator(*foo) ){  // add non-hard argument.
									bool quote  = false;
									if ( *foo == mQuoteChar ){
											foo++;
											quote = true;
									}

									if ( argc < MAXARGS ){
											argv[argc++] = foo;
									}

									if ( quote ){
											while (*foo && *foo != mQuoteChar ) foo++;
											if ( *foo ) *foo = 32;
									}

									// continue..until we hit an eos ..
									while ( !EOS(*foo) ){ // until we hit EOS
											if ( IsWhiteSpace(*foo) ){ // if we hit a space, stomp a zero byte, and exit
													*foo = 0;
													foo++;
													break;
											}else if ( IsHard(*foo) ){ // if we hit a hard separator, stomp a zero byte and store the hard separator argument
													const char *hard = &mHardString[*foo*2];
													*foo = 0;
													if ( argc < MAXARGS ){
															argv[argc++] = hard;
													}
													foo++;
													break;
											}
											foo++;
									} // end of while loop...
							}
					}
			}

			if ( argc ){
					ret = callback->ParseLine(lineno, argc, argv );
			}

			return ret;
	}

	int  InPlaceParser::Parse(InPlaceParserInterface *callback){ // returns true if entire file was parsed, false if it aborted for some reason
			assert( callback );
			if ( !mData ) return 0;

			int ret = 0;

			int lineno = 0;

			char *foo   = mData;
			char *begin = foo;


			while ( *foo ){
					if ( *foo == 10 || *foo == 13 ){
							lineno++;
							*foo = 0;

							if ( *begin ){ // if there is any data to parse at all...
									int v = ProcessLine(lineno,begin,callback);
									if ( v ) ret = v;
							}

							foo++;
							if ( *foo == 10 ) foo++; // skip line feed, if it is in the carraige-return line-feed format...
							begin = foo;
					}else{
							foo++;
					}
			}

			lineno++; // lasst line.

			int v = ProcessLine(lineno,begin,callback);
			if ( v ) ret = v;
			return ret;
	}


	void InPlaceParser::DefaultSymbols(void){
			SetHardSeparator(',');
			SetHardSeparator('(');
			SetHardSeparator(')');
			SetHardSeparator('=');
			SetHardSeparator('[');
			SetHardSeparator(']');
			SetHardSeparator('{');
			SetHardSeparator('}');
			SetCommentSymbol('#');
	}


	const char ** InPlaceParser::GetArglist(char *line,int &count){ // convert source string into an arg list, this is a destructive parse.
			const char **ret = 0;

			static const char *argv[MAXARGS];
			int argc = 0;

			char *foo = line;

			while ( !EOS(*foo) && argc < MAXARGS ){

					foo = SkipSpaces(foo); // skip any leading spaces

					if ( EOS(*foo) ) break;

					if ( *foo == mQuoteChar ){ // if it is an open quote
							foo++;
							if ( argc < MAXARGS ){
									argv[argc++] = foo;
							}
							while ( !EOS(*foo) && *foo != mQuoteChar ) foo++;
							if ( !EOS(*foo) ){
									*foo = 0; // replace close quote with zero byte EOS
									foo++;
							}
					}else{
							foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces

							if ( IsNonSeparator(*foo) ) { // add non-hard argument.
									bool quote  = false;
									if ( *foo == mQuoteChar ){
											foo++;
											quote = true;
									}

									if ( argc < MAXARGS ){
											argv[argc++] = foo;
									}

									if ( quote ){
											while (*foo && *foo != mQuoteChar ) foo++;
											if ( *foo ) *foo = 32;
									}

									// continue..until we hit an eos ..
									while ( !EOS(*foo) ){ // until we hit EOS
											if ( IsWhiteSpace(*foo) ) {// if we hit a space, stomp a zero byte, and exit
													*foo = 0;
													foo++;
													break;
											}else if ( IsHard(*foo) ) {// if we hit a hard separator, stomp a zero byte and store the hard separator argument
													const char *hard = &mHardString[*foo*2];
													*foo = 0;
													if ( argc < MAXARGS ){
															argv[argc++] = hard;
													}
													foo++;
													break;
											}
											foo++;
									} // end of while loop...
							}
					}
			}

			count = argc;
			if ( argc ){
					ret = argv;
			}

			return ret;
	}

	#define MAXPARSEBUFFER 2048

	class MyTelnet : public Telnet{
		public:
			  MyTelnet(const char *address,unsigned int port){
				mParser.DefaultSymbols();
				mClient = 0;
				mInterface = 0;
				mServer = new TelnetServer;
				mIsServer = mServer->Listen(port);
				mHaveConnection = true;
				if ( mIsServer ){
				  mInterface = static_cast< TelnetInterface *>(mServer);
				}else{
				  delete mServer;
				  mServer = 0;
				  mClient = new TelnetClient;
				  mHaveConnection = mClient->Connect(address,port);
				  if ( !mHaveConnection ){
					delete mClient;
					mClient = 0;
				  }else{
					mInterface = static_cast< TelnetInterface *>(mClient);
				  }
				}
			  }

			  virtual bool isServer(void){// returns true if we are a server or a client.  First one created on a machine is a server, additional copies are clients.
				return mIsServer;
			  }

			  virtual bool haveConnection(void){
				return mHaveConnection;
			  }

			  virtual bool sendMessage(unsigned int client,const char *fmt,...){
				bool ret = false;
				if ( mInterface ){
					char wbuff[MAXPARSEBUFFER];
					wbuff[MAXPARSEBUFFER-1] = 0;
					_vsnprintf(wbuff,MAXPARSEBUFFER-1, fmt, (char *)(&fmt+1));
					ret = mInterface->SendText(client,"%s",wbuff);
				}
				return ret;
			  }

			  virtual const char *  receiveMessage(unsigned int &client){
				const char *ret = 0;
				client = 0;

				if ( mInterface ){
				  ret = mInterface->GetLine(client);
				}

				return ret;
			  }

			  virtual const char ** getArgs(const char *input,int &argc){ // parse string into a series of arguments.
				strncpy(mParseBuffer,input,MAXPARSEBUFFER);
				mParseBuffer[MAXPARSEBUFFER-1] = 0;
				return mParser.GetArglist(mParseBuffer,argc);
			  }

			  virtual ~MyTelnet(void){
				delete mClient;
				delete mServer;
			  }
		private:
			  bool          mIsServer;
			  bool          mHaveConnection;
			  TelnetInterface *mInterface;
			  TelnetClient *mClient;
			  TelnetServer *mServer;
			  char          mParseBuffer[MAXPARSEBUFFER];
			  InPlaceParser mParser;
	};

	Telnet * createTelnet(const char *address,unsigned int port){
		MyTelnet *m = new MyTelnet(address,port);
		return static_cast< Telnet *>(m);
	}

	void releaseTelnet(Telnet *t){
		MyTelnet *m = static_cast< MyTelnet *>(t);
		delete m;
	}


}; // end of namespace